#include "stdafx.h"

#include "tdd.h"
#include "tddassert.h"
#include "utcommon.h"
#include <algorithm>

#include "..\\SmartPtr\\SmartPtrManager.h"

static class TestNode : public LinkNode
{
public:
	TestNode(int size = GENERATIONS_2_BYTES_LIM)
	{
		SetSize(size);
	}
};

namespace SmartPtr
{
	namespace UnitTest
	{
		TESTCLASS(CSmartPtrManagerTests)
		{
			virtual void TestCleanup()
			{
				CSmartPtrManager::Destroy();	
			} 

			TESTMETHOD(TestCreateSmartPtrManager)
			{
				TEST_NO_EXCEPTION(CSmartPtrManager::GetInstance());
				TDD_VERIFY(CSmartPtrManager::GetInstance() != NULL);
			}

			TESTMETHOD(TestAddSmartPtr_AddNULL)
			{
				bool bSuccess = false;
				// do not GC
				TDD_VERIFY( CSmartPtrManager::GetInstance()->AddToManager(NULL, bSuccess) == false);
				// failed to insert
				TDD_VERIFY(bSuccess == false);
			}

			TESTMETHOD(TestAddSmartPtr_AddNotNull)
			{
				bool bSuccess = false;
				LinkNode * testNode = new TestNode(2 * GENERATIONS_2_BYTES_LIM);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->AddToManager(testNode, bSuccess) == false);
				TDD_VERIFY(bSuccess == true);

				LinkNode * testNode1 = new TestNode(2 * GENERATIONS_2_BYTES_LIM);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->AddToManager(testNode1, bSuccess) == false);
				TDD_VERIFY(bSuccess == true);

				LinkNode * testNode2 = new TestNode(2 * GENERATIONS_2_BYTES_LIM);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->AddToManager(testNode2, bSuccess) == true);
				TDD_VERIFY(bSuccess == true);

				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsCount(0) == 3);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsSize(0) == 6 * GENERATIONS_2_BYTES_LIM);
				// do not need to delete heap variable, the Destroy function will call this.
			}

			TESTMETHOD(TestAddSmartPtr_AddSame)
			{
				bool bSuccess = false;
				LinkNode * testNode = new TestNode();
				TDD_VERIFY( CSmartPtrManager::GetInstance()->AddToManager(testNode, bSuccess) == false);
				TDD_VERIFY(bSuccess == true);

				TDD_VERIFY( CSmartPtrManager::GetInstance()->AddToManager(testNode, bSuccess) == false);
				// failed to insert the same object
				TDD_VERIFY(bSuccess == false);
			}

			TESTMETHOD(TestAddSmartPtr_AddNotEnoughSpace)
			{
				// this case will never be caught, for the GC will do.
				// after continuous 2 GC and still the generation 2 is full
				// it will throw OutOfMemory exception.
			}

			TESTMETHOD(TestRemoveSmartPtr_RemoveNull)
			{
				// remove from an empty list
				bool bRemoveSuccess = false;
				CSmartPtrManager::GetInstance()->RemveFromManager(NULL, bRemoveSuccess);
				TDD_VERIFY(bRemoveSuccess == false);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsCount(0) == 0);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsSize(0) == 0);


				// remove from an empty list
				LinkNode * linkNode = new TestNode();
				bool bAddSuccess = false;
				CSmartPtrManager::GetInstance()->AddToManager(linkNode, bAddSuccess);
				TDD_VERIFY(bAddSuccess == true);
				CSmartPtrManager::GetInstance()->RemveFromManager(NULL, bRemoveSuccess);
				TDD_VERIFY(bRemoveSuccess == false);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsCount(0) == 1);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsSize(0) == GENERATIONS_2_BYTES_LIM);

			}

			TESTMETHOD(TestRemoveSmartPtr_RemoveExist)
			{
				bool bRemoveSuccess = false;
				bool bInsertSuccess = false;
				LinkNode * linkNode = new TestNode();
				CSmartPtrManager::GetInstance()->AddToManager(linkNode, bInsertSuccess);
				CSmartPtrManager::GetInstance()->RemveFromManager(linkNode, bRemoveSuccess);
				TDD_VERIFY(bRemoveSuccess == true);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsCount(0) == 0);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsSize(0) == 0);

			}

			TESTMETHOD(TestRemoveSmartPtr_RemoveNotExist)
			{
				bool bRemoveSuccess = false;
				bool bInsertSuccess = false;
				LinkNode * linkNode = new TestNode();
				CSmartPtrManager::GetInstance()->AddToManager(linkNode, bInsertSuccess);
				
				LinkNode * linkNode2 = new TestNode();
				CSmartPtrManager::GetInstance()->RemveFromManager(linkNode2, bRemoveSuccess);
				TDD_VERIFY(bRemoveSuccess == false);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsCount(0) == 1);
				TDD_VERIFY(
					CSmartPtrManager::GetInstance()->GetGenerationObjectsSize(0) == GENERATIONS_2_BYTES_LIM);
				DELETE_IF_NOT_NULL(linkNode2);
			}
			
			TESTMETHOD(TestShiftPtrs_NoGC)
			{
				// the default is in generation 0
				LinkNode* linkNode = new TestNode();
				LinkNode* linkNode2 = new TestNode();				

				list<LinkNode*> tempList;
				tempList.push_front(linkNode);
				tempList.push_front(linkNode2);

				int iNeedGc = -1;
				iNeedGc = CSmartPtrManager::GetInstance()->ShiftGcSurvivePtrs(0, tempList);
				// the generation 1 originally is empty, then 2 * GENERATIONS_2_BYTES_LIM has been put into 
				// it, but it can contains for 3 * GENERATIONS_2_BYTES_LIM. So will not cause GC
				TDD_VERIFY(iNeedGc == -1);

				// we do not need to delete the linkNode
			}

			TESTMETHOD(TestShiftPtrs_GC)
			{
				// the default is in generation 0
				// the following is enough to expand the generation 1
				LinkNode* linkNode = new TestNode(GENERATIONS_0_BYTES_LIM);
				LinkNode* linkNode2 = new TestNode(GENERATIONS_0_BYTES_LIM);				

				list<LinkNode*> tempList;
				tempList.push_front(linkNode);
				tempList.push_front(linkNode2);

				int iNeedGc = -1;
				iNeedGc = CSmartPtrManager::GetInstance()->ShiftGcSurvivePtrs(0, tempList);

				// the generation 1 needs to do GC
				TDD_VERIFY(iNeedGc == 1);

				// we do not need to delete the linkNode
			}
			
			TESTMETHOD(TestCollectGcPtrs_G0)
			{
				bool bRemoveSuccess = false;
				bool bInsertSuccess = false;

				LinkNode * linkNode = new TestNode(2 * GENERATIONS_2_BYTES_LIM);
				CSmartPtrManager::GetInstance()->AddToManager(linkNode, bInsertSuccess);	
				TDD_VERIFY(bInsertSuccess == true);

				LinkNode * linkNode2 = new TestNode(2 * GENERATIONS_2_BYTES_LIM);
				CSmartPtrManager::GetInstance()->AddToManager(linkNode2, bInsertSuccess);	
				TDD_VERIFY(bInsertSuccess == true);

				LinkNode * linkNode3 = new TestNode(GENERATIONS_2_BYTES_LIM);
				CSmartPtrManager::GetInstance()->AddToManager(linkNode3, bInsertSuccess);	
				TDD_VERIFY(bInsertSuccess == true);

				list<LinkNode*> tempList;
				CSmartPtrManager::GetInstance()->CollectNeedGcPtrs(0, tempList);
				int countNum = 0;
				for (list<LinkNode*>::iterator it = tempList.begin();
					it != tempList.end();
					it++)
				{
					countNum++;
					TDD_VERIFY(count(tempList.begin(), tempList.end(), (*it)) == 1);
				}
				TDD_VERIFY(countNum == 3);
			}

			TESTMETHOD(TestCollectGcPtrs_G1)
			{
				bool bRemoveSuccess = false;
				bool bInsertSuccess = false;

				LinkNode * linkNode = new TestNode(2 * GENERATIONS_2_BYTES_LIM);
				CSmartPtrManager::GetInstance()->AddToManager(linkNode, bInsertSuccess);	
				TDD_VERIFY(bInsertSuccess == true);

				LinkNode * linkNode2 = new TestNode(2 * GENERATIONS_2_BYTES_LIM);
				CSmartPtrManager::GetInstance()->AddToManager(linkNode2, bInsertSuccess);	
				TDD_VERIFY(bInsertSuccess == true);

				LinkNode * linkNode3 = new TestNode(GENERATIONS_2_BYTES_LIM);
				CSmartPtrManager::GetInstance()->AddToManager(linkNode3, bInsertSuccess);	
				TDD_VERIFY(bInsertSuccess == true);

				list<LinkNode*> tempList;
				CSmartPtrManager::GetInstance()->CollectNeedGcPtrs(0, tempList);
				int countNum = 0;
				for (list<LinkNode*>::iterator it = tempList.begin();
					it != tempList.end();
					it++)
				{
					countNum++;
					TDD_VERIFY(count(tempList.begin(), tempList.end(), (*it)) == 1);
				}
				TDD_VERIFY(countNum == 3);
			}

			TESTMETHOD(TestCollectGcPtrs_ClearOriginal)
			{
				// enough to expand the g1
				LinkNode* linkNode = new TestNode(GENERATIONS_1_BYTES_LIM + 1);

				list<LinkNode*> tempList;
				tempList.push_front(linkNode);

				// assume we have done a GC towards g0
				int iNeedGc = -1;
				iNeedGc = CSmartPtrManager::GetInstance()->ShiftGcSurvivePtrs(0, tempList);
				// now the linkNode is in the generation 1
				TDD_VERIFY(iNeedGc == 1);
				
				CSmartPtrManager::GetInstance()->CollectNeedGcPtrs(iNeedGc, tempList);
				TDD_VERIFY((*tempList.begin()) == linkNode);
				// list for the generation 1 must be empty
				// attention that the CollectNeedGcPtrs will not cause the generation to be empty
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsCount(1) == 1);
				TDD_VERIFY(CSmartPtrManager::GetInstance()->GetGenerationObjectsSize(1) == GENERATIONS_1_BYTES_LIM + 1);
			}
		};
	}
}